package com.bruce.starts.demo.commonerror.spring;


import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Array;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * @author luotuan
 * @Description
 * @create 2021-05-12 16:34
 **/
@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnProperty(value = "customer.metric.flag",havingValue = "true")
public class MetricsAspect {
    private static final Map<Class<?>, Object> DEFAULT_VALUE = Stream
            .of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class)
            .collect(Collectors.toMap(clazz -> (Class<?>) clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));
    @Autowired
    private ObjectMapper objectMapper;
    private static  <T> T getDefaultValue(Class<T> tClass){
        return (T) DEFAULT_VALUE.get(tClass);
    }

    @Pointcut("within(@com.bruce.starts.demo.commonerror.spring.Metrics *)")
    public void withMetricsAnnotation(){

    }
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void controllerBean(){

    }
    @Around("withMetricsAnnotation() || controllerBean()")
    public Object metrics(ProceedingJoinPoint joinPoint) throws Throwable {
        //尝试获取当前方法的类名和方法名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String name = String.format("【%s】【%s】",methodSignature.getDeclaringType().toString(),methodSignature.toLongString());
        Metrics metrics = methodSignature.getMethod().getAnnotation(Metrics.class);
        if(metrics==null){
            methodSignature.getMethod().getDeclaringClass().getAnnotation(Metrics.class);
        }
        if(metrics==null){
            @Metrics
            final class c{

            }
            metrics = c.class.getAnnotation(Metrics.class);
        }
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if(requestAttributes!=null){
            HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
            if(request!=null){
                name+= String.format("【%s】",request.getRequestURL().toString());
            }
        }
        if(metrics.logParameters()){
            log.info(String.format("【入参日志】调用%s的参数是：【%s】",name,objectMapper.writeValueAsString(joinPoint.getArgs())));
        }
        Object returnValue = null;
        Instant start = Instant.now();
        try {
            returnValue = joinPoint.proceed();
            if(metrics.recordSuccessMetrics()){
                log.info(String.format("【成功打点】调用%s成功，耗时：%d ms",name, Duration.between(start,Instant.now()).toMillis()));
            }
        }
        catch (Exception ex) {
            if (metrics.recordFailMetrics()){
                log.info(String.format("【失败打点】调用%s失败，耗时：%d ms", name, Duration.between(start, Instant.now()).toMillis()));
            }
            if (metrics.logException()){
                log.error(String.format("【异常日志】调用 %s 出现异常！", name), ex);
            }
            if(metrics.ignoreException()){
                returnValue = getDefaultValue(methodSignature.getReturnType());
            }else {
                throw ex;
            }
        }
        if(metrics.logReturn()){
            log.info(String.format("【出参日志】调用 %s 的返回是：【%s】", name, returnValue));
        }
        return returnValue;
    }
}
